use Entity;
package EntityBag;

sub new{
    my $class = shift;

    my $self = {};
    bless $self, $class;
    $self->init($class, @_);

    
    return $self;
} # new

# Basic init
sub init{
    my ($self, $class) = (shift, shift);

    $self->{CONTENTS} = {};
    $self->{CONTENT_LIST} = {};
}

sub add{
    my $self = shift;
    my ($ref, $category) = (shift, shift);

    $category = $ref->type unless(defined($category));

    # Check if entity ref is already in bag
    my $contentListElement = $self->{CONTENT_LIST}->{$ref};
    my $catList;
    if(defined($contentListElement)){
	# if it was, remove from the old category
	$oldCat = $contentListElement->{CATEGORY};
	$catList = $self->{CONTENTS}->{$oldCat};
	delete($catList->[$contentListElement->{INDEX}]);
    }
    
    # Find the list of entities in the category
    $catList = $self->{CONTENTS}->{$category};
    
    # Create and assign if this is a new category 
    if(!defined($catList)){
	$catList = [];
	$self->{CONTENTS}->{$category} = $catList;
    }
    
    # Add to category
    push(@{$catList}, $ref);

    # Find new index (thread unsafe unless the list is synchronized :-o)
    $newIndexInCat = scalar(@{$catList})-1;

    $contentListElement = {REF=>$ref, CATEGORY=>$category, INDEX=>$newIndexInCat};
    $self->{CONTENT_LIST}->{$ref} = $contentListElement;
}

sub remove{
    my $self = shift;
    my $ref = shift;

    if(!exists($self->{CONTENT_LIST}->{$ref})){
	return;
    }
    my $contentListElement = $self->{CONTENT_LIST}->{$ref};
    my $catList = $self->{CONTENTS}->{$contentListElement->{CATEGORY}};

    delete($catList->[$contentListElement->{INDEX}]);
    delete($self->{CONTENT_LIST}->{$ref});
} # remove

# Returns a reference to a COPY of the
# contents of the given category, if it exists
sub category{
    my ($self, $cat) = (shift, shift);

    if(exists($self->{CONTENTS}->{$cat})){
	my @copy = (@{$self->{CONTENTS}->{$cat}});
	return  \@copy;
    }else{
	return [];
    }
}

# Returns a reference to a COPY of the list
# of the total contents of the bag
sub all{
    my ($self) = (shift);
    
    my @result = ();
    my $contentListElem;
    foreach $contentListElem (values(%{$self->{CONTENT_LIST}})){
	push(@result, $contentListElem->{REF});
    }

    return \@result;
}

# Finds entities that match given criterias on the category they are in
# and/or in their attributes
# Parameters: 
#   A reference to a hash of attribute names and values (if undef, no attributes are compared)
#   A reference to a list of Category names (if undef or empty, all categories are searched)
#
sub find{
    my $self = shift;
    
    my ($attributes, $categories) = (shift, shift);

    my @candidates = ();
    if(defined $categories){
	my $cat_list;
	foreach my $cat(@{$categories}){
	    $cat_list = $self->category($cat);
	    push @candidates, @{$cat_list};
	}
    }else{
	@candidates = @{$self->all};
    }
    
    if((scalar(keys %{$attributes}) == 0) || !defined($attributes)){
	return \@candidates;
    }

    my @found = ();
    foreach my $entity(@candidates){
	foreach my $attr(keys %{$attributes}){
	    push @found, $entity if($entity->attr($attr) == $attributes->{$attr});
	}
    }

    return \@found;
}

# Returns the name of the category where
# the entity is contained, or the empty
# string if it`s not in the bag
sub where{
    my ($self, $ref) = (shift, shift);
    
    my $contentListElement = $self->{CONTENT_LIST}->{$ref};
    
    return $contentListElement->{CATEGORY} if(defined($contentListElement));
    return '';
} # where

1;
